﻿using MicroCloud.Filter;
using Microsoft.EntityFrameworkCore;
using System.IO;

namespace MicroCloud.Systems
{
    /// <summary>
    /// 业务实现：文件服务类
    /// </summary>
    public class FileService : IFileService
    {
        #region 属性
        /// <summary>
        /// 获取 服务提供者对象
        /// </summary>
        protected IServiceProvider ServiceProvider { get; }

        /// <summary>
        /// 获取 日志对象
        /// </summary>
        protected ILogger Logger => ServiceProvider.GetLogger(GetType());

        /// <summary>
        /// 获取 事件总线
        /// </summary>
        protected IEventBus EventBus => ServiceProvider.GetService<IEventBus>();

        /// <summary>
        /// 获取 过滤表达式功能
        /// </summary>
        protected IFilterService FilterService => ServiceProvider.GetService<IFilterService>();

        /// <summary>
        /// 获取 文件配置选项
        /// </summary>
        protected FileOption FileOption => ServiceProvider.GetService<IConfiguration>()?.GetFileOption();

        /// <summary>
        /// 获取 文件存储管理
        /// </summary>
        protected FileStore FileStoreManage => ServiceProvider.GetService<FileStore>();
        #endregion

        /// <summary>
        /// 初始化一个文件服务类（<see cref="FileService"/>）的新实例
        /// </summary>
        /// <param name="provider">服务提供程序</param>
        public FileService(IServiceProvider provider)
        {
            ServiceProvider = provider;
        }

        #region 接口方法
        /// <summary>
        /// 获取映射到静态资源的相对请求路径
        /// </summary>
        /// <returns></returns>
        public virtual string GetRequestPath()
        {
            return FileOption.RequestPath;
        }

        public virtual async Task<FileOutputDto> GetFileInfo(string id)
        {
            var file = await GetFile(id);
            return file?.MapTo<FileOutputDto>();
        }


        public virtual async Task<FileOutputDto> GetFileInfoByName(string fileName)
        {
            var file = await GetFileByName(fileName);
            return file?.MapTo<FileOutputDto>();
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="dto">上传文件DTO</param>
        /// <returns></returns>
        public virtual async Task<OperationResult<FileOutputDto>> UploadAsync(UploadInputDto dto)
        {
            var operation = dto.CheckOption(FileOption);
            if (!operation.Succeeded) return operation.ToOperationResult<FileOutputDto>();

            var serializedName = $"{Guid.NewGuid().ToString("N").ToUpper()}.{dto.FileExtension}";
            FileEntity file = new FileEntity()
            {
                FileName = dto.FileName,
                FileExtension = dto.FileExtension?.ToLower(),
                Version = 0,
                SHA1 = HashHelper.GetStreamSHA1(dto.FileStream),
                MD5 = HashHelper.GetStreamMD5(dto.FileStream),
                Size = dto.FileStream.Length,
                SerializedName = serializedName,
                RequestPath = FileOption.GetRequestPath(serializedName),
                PhysicalPath = FileOption.GetPhysicalPath(serializedName)
            };

            operation = await FileStoreManage.CreateAsync(file);
            if (!operation.Succeeded) return operation.ToOperationResult<FileOutputDto>();

            var physicalFullPath = FileOption.GetPhysicalFullPath(serializedName);
            await FileHelper.SaveAsync(physicalFullPath, dto.FileStream);

            //新增文件操作记录
            operation = await AddFileActionRecord(file, FileActionType.Upload);
            if (!operation.Succeeded) return operation.ToOperationResult<FileOutputDto>();

            return OperationResult.Success(file.MapTo<FileOutputDto>());
        }

        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="id">文件编号</param>
        /// <returns></returns>
        public virtual async Task<OperationResult<DownloadOutputDto>> DownloadAsync(string id)
        {
            Check.NotNullOrEmpty(id, nameof(id));
            FileEntity file = await GetFile(id);
            if (file == null) return OperationResult.Error<DownloadOutputDto>($"文件Id：{id},不存在");

            Stream stream = new FileStream(Path.Combine(file.PhysicalPath, file.SerializedName),
                FileMode.Open, FileAccess.Read, FileShare.Read);

            // 新增文件操作记录
            await AddFileActionRecord(file, FileActionType.Download);

            var downloadDto = new DownloadOutputDto
            {
                FileStream = stream,
                FileName = file.FileName,
                FileExtension = file.FileExtension,
            };
            return OperationResult.Success(downloadDto);
        }

        /// <summary>
        /// 更新文件
        /// </summary>
        /// <param name="dto">更新文件DTO</param>
        /// <returns></returns>
        public virtual async Task<OperationResult<FileOutputDto>> UpdateAsync(UploadInputDto dto)
        {
            OperationResult operation = dto.CheckOption(FileOption);
            if (!operation.Succeeded) return operation.ToOperationResult<FileOutputDto>();

            FileEntity file = await GetFile(dto.Id);
            if (file == null) return OperationResult.Error<FileOutputDto>($"文件Id：{dto.Id},不存在");

            //step1:存档数据及删除文件
            await AddFileSaveRecord(file);

            #region step2:更新数据及保存文件
            var serializedName = $"{Guid.NewGuid().ToString("N").ToUpper()}.{dto.FileExtension}";
            string routeOption = FileOption.GetRouteOption();
            file.FileName = dto.FileName;
            file.FileExtension = dto.FileExtension;
            file.Version += 1;
            file.SerializedName = serializedName;
            file.SHA1 = HashHelper.GetStreamSHA1(dto.FileStream);
            file.MD5 = HashHelper.GetStreamMD5(dto.FileStream);
            file.Size = dto.FileStream.Length;
            file.RequestPath = FileOption.GetRequestPath(serializedName);
            file.PhysicalPath = FileOption.GetPhysicalPath(serializedName);
            operation = await FileStoreManage.UpdateAsync(file);
            if (!operation.Succeeded) return operation.ToOperationResult<FileOutputDto>();

            var physicalFullPath = FileOption.GetPhysicalFullPath(serializedName);
            await FileHelper.SaveAsync(physicalFullPath, dto.FileStream);
            #endregion

            #region step3:新增文件操作记录
            operation = await AddFileActionRecord(file, FileActionType.Update);
            if (!operation.Succeeded) return operation.ToOperationResult<FileOutputDto>();
            #endregion

            return OperationResult.Success<FileOutputDto>(file.MapTo<FileOutputDto>());
        }

        /// <summary>
        /// 删除文件
        /// </summary>
        /// <param name="id">文件编号</param>
        /// <returns></returns>
        public virtual async Task<OperationResult> DeleteAsync(string id)
        {
            Check.NotNullOrEmpty(id, nameof(id));

            FileEntity file = await GetFile(id);
            if (file == null) return OperationResult.Error($"文件Id：{id},不存在");

            //step1:物理文件存档并删除
            await AddFileSaveRecord(file);

            //step2:删除文件表数据
            OperationResult operation = await FileStoreManage.DeleteAsync(file);
            if (!operation.Succeeded) return operation;

            //step3: 新增文件操作记录
            operation = await AddFileActionRecord(file, FileActionType.Update);
            if (!operation.Succeeded) return operation;

            return OperationResult.Success($"文件Id:{id},删除成功");
        }
        #endregion

        #region 辅助方法     
        /// <summary>
        /// 根据文件ID获取文件信息
        /// </summary>
        /// <param name="id">文件编号</param>
        /// <returns></returns>
        protected virtual async Task<FileEntity> GetFile(string id)
        {
            Check.NotNullOrEmpty(id, nameof(id));
            return await FileStoreManage.FindByIdAsync(id);
        }

        /// <summary>
        /// 根据文件名称获取文件信息
        /// </summary>
        /// <param name="fileName">文件名称</param>
        /// <returns></returns>
        protected virtual async Task<FileEntity> GetFileByName(string fileName)
        {
            Check.NotNullOrEmpty(fileName, nameof(fileName));
            return await FileStoreManage.Files.FirstOrDefaultAsync(a => a.FileName == fileName.Trim());
        }

        /// <summary>
        /// 新增文件存档
        /// </summary>
        protected virtual async Task<OperationResult> AddFileSaveRecord(FileEntity file)
        {
            Check.NotNull(file, nameof(file));

            OperationResult result = OperationResult.Success();
            if (!FileOption.VersionEnabled) return result;

            var fullPath = Path.Combine(FileOption.Root, file.PhysicalPath?.TrimStart(new char[] { '\\' }));
            FileSaveRecord saveRecord = new FileSaveRecord
            {
                FileId = file.Id,
                FileName = file.FileName,
                FileExtension = file.FileExtension,
                Size = file.Size,
                SHA1 = file.SHA1,
                MD5 = file.MD5,
                Version = file.Version,
                SerializedName = file.SerializedName,
                RequestPath = file.RequestPath,
                PhysicalPath = file.PhysicalPath,
                FileByte = FileHelper.GetFileBytes(fullPath),
            };
            result = await FileStoreManage.AddSaveRecordAsync(saveRecord);
            if (result.Succeeded)
            {
                FileHelper.Delete(fullPath);
            }
            return result;
        }

        /// <summary>
        /// 新增文件操作记录
        /// </summary>
        protected virtual async Task<OperationResult> AddFileActionRecord(FileEntity file, FileActionType actionType)
        {
            OperationResult result = await FileStoreManage.AddActionRecordAsync(new FileActionRecord
            {
                FileId = file.Id,
                Version = file.Version,
                ActionType = actionType
            });
            return result;
        }  
        #endregion
    }
}
